/*#define _DEBUG_MEM_*/

/******************
 *  init_alloc()
 *  inicializace prace s pameti
 */
int init_alloc(void) {
  char far *i;

  __brklvl[KERNEL_SEGMENT >> 12] = 0x6000;
  mem_alloc_bottom[KERNEL_SEGMENT] = NULL;
  for(i = (char far *)(KERNEL_LSEGMENT + __brklvl[KERNEL_SEGMENT >> 12]);
      i<= (char far *)(KERNEL_LSEGMENT + 0xF000);
      i++)
    *i = '\0';

  return 0;
}
#ifdef _DEBUG_MEM_
static void far * ks = 0;
static unsigned int kl = 0;
static void far * os = 0;
static unsigned int ol = 0;
#endif
/**
 *  mem_alloc()
 *  alokuje blok pameti na segmentu <seg> o delce <length>
 */
void far *mem_alloc(unsigned int length, unsigned int seg) {
  block	far *akt, far *next;
  unsigned int size;
  block	far *ltop = (block far *)((char far *)((char far *)((unsigned long)seg << 16) + (unsigned int)sbrk(0)));
#ifdef _DEBUG_MEM_
  int x,y;
#endif
  if (mem_alloc_bottom[seg >> 12] == NULL)
    mem_alloc_bottom[seg >> 12] = ltop;

  length = (length + sizeof(unsigned int) + BUSY) & ~BUSY;

  akt = mem_alloc_bottom[seg >> 12];
  for (;;) {
    if (akt == ltop) {
      next = (block far *)&akt->data[length];
      if (next < akt || brk((int)next) == -1)
        return NULL;
      break;
    }

    next = (block far *)(&akt->data[akt->length & ~BUSY]);

    if (isbusy(akt)) {
      akt = next;
    } else if (next == ltop || isbusy(next)) {
      size = akt->length;
      if (size >= length) {
        if (size > length) {
          ((block far *)&akt->data[length])->length = size - length;
        }
        break;
      }
      akt = next;
    }
    else {
      akt->length += next->length;
    }
  }
  akt->length = length | BUSY;
#ifdef _DEBUG_MEM_
  getxy(&x,&y);
  gotoxy(0,0);
  if(seg == 0x1000) {
    ks = &akt->data[sizeof(unsigned int)];
    kl = length;
  }
  else {
    os = &akt->data[sizeof(unsigned int)];
    ol = length;
  }
  kprintf("   K[0x%x|0x%x|%i|{0x%x}] | S[0x%x|0x%x|%i]           ", (void *)ks, (int)__brklvl[1], kl, _SP, (void *)os, (int)__brklvl[3], ol);
  gotoxy(x,y);
#endif
  return &akt->data[sizeof(unsigned int)];
}

/**
 *  mem_free()
 *  dealokuje blok pameti
 */
void mem_free(void far *ptr) {
  if (ptr) {
    ((char far *) ptr)[-sizeof(unsigned int)] &= ~BUSY;
  }
}

/**
 *  mem_realloc()
 *  dealokuje aktualni blok, alokuje novy o delce <newlength>, zkopiruje
 *  data z puvodni pameti a vrati novy pointer
 */
void far *mem_realloc(void far *ptr, unsigned int newlength) {
  unsigned int oldlength = ((unsigned int far *)ptr)[-1] & ~BUSY;
  void far *newptr;

  newptr = mem_alloc(newlength, (int)(((long)((long)ptr >> 16)) & 0xFFFF));

  if (newptr == NULL) {
    if (newlength <= oldlength)
      return ptr;
  } else {
    memcpy(newptr, ptr, oldlength);
  }

  mem_free(ptr);
  return newptr;
}
